/**
 * List.java
 * Autores: Jennifer Valdez 10189
 *          Gustavo Sánchez 10313
 *          Sergio Molina   10256
 * Fecha de entrega: 21 de noviembre de 2011
 * Descripcion: Es la clase que evalua las expresiones del lisp y las opera.
 * 
 */

//librerias utilizadas
import java.util.HashMap;
import java.util.LinkedList;

public class List {
 
    // atributos
    private String[] expresion;  //La expresion a evualuar al llamar a evaluar()
    private int index;          // Contador que indica por que parte del String vamos
    private HashMap funciones;  // HashMap que tiene todas las funciones declaradas hasta el momento
    
    /**
     * Constructor coloca el index en 0 e inicializa expresion y funciones
     * @param instru El nuevo valor de expresion
     * @param funcionesActuales El nuevo valor de funciones
     */    
    public List(String[] instru, HashMap funcionesActuales){
        expresion = instru;
        index = 0;
        funciones = funcionesActuales;
    }
    
    /**
     * Se cambia el valor de expresion
     * @param instru Nuevo valor de expresion
     */
    public void setExpresion(String[] instru){
        expresion = instru;
    }
    
    /**
     * Se obtiene el valor de expresion
     * @return El valor de la expresion;
     */
    public String[] getExpresion(){
        return expresion;
    }
    
    /**
     *  Se lee casilla por casilla de expresion evaluando si debe sumar, restar,
     *  multiplicar o dividir, de no ser ninguno evalua si es una funcion.
     * @return El resultado de la operacion
     */
    public String evaluar(){
        LinkedList lista = new LinkedList();
        String resultado="";
        double result;
        index++;
        try{
            //Verifica si el caracter '('
            if (expresion[index].equals("("))
                return evaluar();       //Se llama recursivamente
            else{
                if (expresion[index].length() == 1){
                    //se evalua que operacion aritmetica se realizara
                    switch(expresion[index].charAt(0)){
                        //Suma
                        case '+':
                            while (!expresion[++index].equals(")")){
                                if (expresion[index].equals("("))
                                    lista.add(evaluar());           //Se llama recursivamente
                                else
                                    lista.add(expresion[index]);    //Se introduce a la lista
                            }
                            result = 0;
                            result = lista.isEmpty()?result:Double.parseDouble((String)lista.remove());
                            while (!lista.isEmpty())
                                result += Double.parseDouble((String)lista.remove());
                            resultado=""+result;
                            break;
                        //Resta
                        case '-':
                            while (!expresion[++index].equals(")")){
                                if (expresion[index].equals("("))
                                    lista.add(evaluar());       //Se llama recursivamente
                                else
                                    lista.add(expresion[index]);    //Se agrega a la lista
                            }
                            result = 0;
                            result = lista.isEmpty()?result:Double.parseDouble((String)lista.remove());
                            while (!lista.isEmpty())
                                result -= Double.parseDouble((String)lista.remove());
                            resultado=""+result;
                            break;
                        //Multiplicacion
                        case '*':
                            while (!expresion[++index].equals(")")){
                                if (expresion[index].equals("("))
                                    lista.add(evaluar());           //Se llama recursivamente
                                else
                                    lista.add(expresion[index]);
                            }
                            result = 0;
                            result = lista.isEmpty()?result:Double.parseDouble((String)lista.remove());
                            while (!lista.isEmpty())
                                result *= Double.parseDouble((String)lista.remove());
                            resultado=""+result;
                            break;
                        //Division      
                        case '/':
                            while (!expresion[++index].equals(")")){
                                if (expresion[index].equals("("))
                                    lista.add(evaluar());           //Se llama recursivamente
                                else
                                    lista.add(expresion[index]);    //Se agrega a la lista
                            }
                            result = 0;
                            result = lista.isEmpty()?result:Double.parseDouble((String)lista.remove());
                            while (!lista.isEmpty())
                                result /= Double.parseDouble((String)lista.remove());
                            resultado=""+result;
                            break;
                        default:
                            //De no ser una operacion aritmetica se evalua que no sea una funcion conocida
                            if (funciones.containsKey(expresion[index])){
                                Funcion function = (Funcion)funciones.get(expresion[index]);
                                String[] param = new String[function.getKeysLength()];
                                int cont = 0;
                                while (!expresion[++index].equals(")")){
                                    param[cont++] = expresion[index];
                                }
                                return function.llamarFuncion(param, funciones);
                            }
                            else
                                return "Esa expresion no existe";
                    }
                    return resultado;
                }
                else{ // no es una operacion aritmética
                    if (funciones.containsKey(expresion[index])){  //Se verifica si no es una funcion declarada
                        Funcion function = (Funcion)funciones.get(expresion[index]);
                        String[] param = new String[function.getKeysLength()];
                        int cont = 0;
                        while (!expresion[++index].equals(")")){
                            param[cont++] = expresion[index];  // se obtienen los parametros
                        }
                        return function.llamarFuncion(param, funciones);  // se evalua el cuerpo de la funcion
                    }
                    else
                        if (expresion[index].equals("EXIT")) // escogio salir del programa
                            resultado = "(EXIT)";
                        else 
                            return "Esa expresion no existe";

                    return resultado;
                }
            }
        }catch (Exception e){
            return "Posee un error en la sintaxis"; // si existe un error entonces fue porque posee un error de sintaxis
        }
    }
    
}